﻿#region 文件信息

/*----------------------------------------------------------------
//
// 文件名称：
// 文件功能描述：
// 设计要求：
//
// 文 件 名：    VampirewalCoreEventBusExtension
// 创建者：      杨程
// 创建日期：	    2023/2/22 22:27:53

//----------------------------------------------------------------*/

#endregion

using Microsoft.Extensions.DependencyInjection;
using System.Reflection;

namespace Vampirewal.Core.EventBus;

/// <summary>
///
/// </summary>
public static class VampirewalCoreEventBusExtension
{
    /// <summary>
    /// 将事件总线工厂注册到IoC中
    /// </summary>
    /// <param name="services"></param>
    public static void AddVampirewalCoreEventBusFactory(this IServiceCollection services)
    {
        Type[]? types = AppDomain.CurrentDomain.GetAssemblies().SelectMany(a => a.GetTypes()).Where(t => t.GetInterfaces().Contains(typeof(IVampirewalCoreEventFactory))).ToArray();

        AddVampirewalCoreEventBusFactory(services, factory =>
        {
            foreach (var type in types)
            {
                factory.Add(type);
            }
        });
    }

    /// <summary>
    /// 如果事件工厂放在其他项目内，可能自动全部加载的方式无法获取到，可通过这个方法加载
    /// </summary>
    /// <param name="services"></param>
    /// <param name="eventFactorys"></param>
    public static void AddVampirewalCoreEventBusFactory(this IServiceCollection services, Action<List<Type>> eventFactorys)
    {
        List<Type> types = new List<Type>();

        eventFactorys?.Invoke(types);

        foreach (Type type in types)
        {
            if (type != null)
            {
                //var methods = type.GetMethods(BindingFlags.Public);
                var ErrorMethods = type.GetMethods()
                                                  .Where(w => w.GetCustomAttribute<EventBusExcuteAttribute>() != null && (w.GetParameters().Length != 1 || w.GetParameters()[0].ParameterType != typeof(EventContext)))
                                                  .Select(s => $"{type.Name}事件工厂内的{s.Name}方法参数数量不等于1或参数类型不是EventContext！请修改！")
                                                  .ToList();

                if (ErrorMethods != null && ErrorMethods.Count > 0)
                {
                    throw new VampirewalCoreEventBusException(string.Join("\r\n", ErrorMethods));
                }

                //注册事件工厂
                services.AddSingleton(type);
            }
        }

        //注册事件总线工厂
        services.AddSingleton<IVampirewalCoreEventBusFactory, VampirewalCoreEventBusFactory>();
    }
}

/// <summary>
/// VampirewalCore事件总线工厂接口
/// </summary>
public interface IVampirewalCoreEventBusFactory
{
    /// <summary>
    /// 推送
    /// </summary>
    /// <param name="EventFactoryName">事件工厂Type名称</param>
    /// <param name="Params"></param>
    void Publish(string EventFactoryName, object? Params = null);

    /// <summary>
    /// 推送
    /// </summary>
    /// <typeparam name="TEventFactory"></typeparam>
    /// <param name="Params"></param>
    void Publish<TEventFactory>(object? Params = null) where TEventFactory : IVampirewalCoreEventFactory;

    /// <summary>
    /// 并行推送
    /// </summary>
    /// <param name="EventFactoryName"></param>
    /// <param name="Params"></param>
    void PublishParallel(string EventFactoryName, object? Params = null);

    /// <summary>
    /// 并行推送
    /// </summary>
    /// <typeparam name="TEventFactory"></typeparam>
    /// <param name="Params"></param>
    void PublishParallel<TEventFactory>(object? Params = null) where TEventFactory : IVampirewalCoreEventFactory;
}

/// <summary>
/// VampirewalCore事件总线工厂
/// </summary>
internal class VampirewalCoreEventBusFactory : IVampirewalCoreEventBusFactory
{
    private readonly IServiceProvider Service;

    private Dictionary<string, Type> EventBusFactoryTypes { get; set; }

    public VampirewalCoreEventBusFactory(IServiceProvider provider)
    {
        Service = provider;

        EventBusFactoryTypes = new Dictionary<string, Type>();
        RegisterAll();
    }

    /// <summary>
    /// 注册全部
    /// </summary>
    private void RegisterAll()
    {
        Type[]? types = AppDomain.CurrentDomain.GetAssemblies().SelectMany(a => a.GetTypes()).Where(t => t.GetInterfaces().Contains(typeof(IVampirewalCoreEventFactory))).ToArray();

        foreach (Type type in types)
        {
            if (type != null)
            {
                EventBusFactoryTypes.Add(type.Name, type);
            }
        }
    }

    public void Publish(string EventFactoryName, object? Params = null)
    {
        if (EventBusFactoryTypes.TryGetValue(EventFactoryName, out Type type))
        {
            var im = Service.GetService(type) as IVampirewalCoreEventFactory;

            if (im == null)
                throw new VampirewalCoreEventBusException($"未找到类型为{type}的事件工厂！");

            //获取当前事件工厂内的所有标记了EventBusExcuteAttribute的方法
            var methods = type.GetMethods().Where(w => w.GetCustomAttribute<EventBusExcuteAttribute>() != null).ToList();

            if (methods != null && methods.Count() == 0)
            {
                throw new VampirewalCoreEventBusException($"该事件工厂中没有找到标记EventBusExcuteAttribute特性的方法！");
            }
            else if (methods != null)
            {
                methods.Sort((s1, s2) =>
                {
                    var attr1 = s1.GetCustomAttribute<EventBusExcuteAttribute>();
                    var attr2 = s2.GetCustomAttribute<EventBusExcuteAttribute>();

                    if (attr1.ExcuteSort > attr2.ExcuteSort)
                    {
                        return 1;
                    }
                    else
                    {
                        return -1;
                    }
                });

                //new一个事件工厂上下文
                EventFactoryContext FactoryContexts = new EventFactoryContext();

                foreach (var sub in methods)
                {
                    //new一个事件上下文，方便传递数据
                    EventContext context = new EventContext()
                    {
                        PassData = Params,
                        MethodName = sub.Name,
                        ExcutingTime = DateTime.Now
                    };

                    //将事件上下文添加到事件工厂的上下文集合中
                    FactoryContexts.EventContexts.Add(context);
                    //获取到当前方法上的AOP特性
                    var attrs = sub.GetCustomAttributes().Where(w => w is EventBusAOPAttribute).ToList();

                    try
                    {
                        //循环执行AOP特性的执行前方法
                        foreach (EventBusAOPAttribute item in attrs)
                        {
                            item.ExcuteBefore(context);
                        }

                        sub.Invoke(im, new object[] { context });
                    }
                    catch (Exception ex)
                    {
                        //捕获方法执行中的异常信息
                        context.ExceptionInfo = ex.InnerException;
                        //将事件工厂的是否全部完成成功标记为false
                        FactoryContexts.IsEventFactoryComplateSuccess = false;
                    }
                    finally
                    {
                        context.ExcutedTime = DateTime.Now;
                        //循环执行AOP特性的执行后方法
                        foreach (EventBusAOPAttribute item in attrs)
                        {
                            item.ExcuteAfter(context);
                        }

                        Params = context.PassData;
                    }
                }

                //在事件工厂内的方法全部都执行完成之后，执行事件工厂的完成方法
                im.EventFactoryComplate(FactoryContexts);
            }
        }
        else
        {
            throw new VampirewalCoreEventBusException($"未找到{EventFactoryName}对应的事件工厂！");
        }
    }

    public void Publish<TEventFactory>(object? Params = null) where TEventFactory : IVampirewalCoreEventFactory
    {
        Type type = typeof(TEventFactory);

        Publish(type.Name, Params);
    }

    public void PublishParallel(string EventFactoryName, object? Params = null)
    {
        if (EventBusFactoryTypes.TryGetValue(EventFactoryName, out Type type))
        {
            var im = Service.GetService(type) as IVampirewalCoreEventFactory;

            if (im == null)
                throw new VampirewalCoreEventBusException($"未找到类型为{type}的事件工厂！");

            //获取当前事件工厂内的所有标记了EventBusExcuteAttribute的方法
            var methods = type.GetMethods().Where(w => w.GetCustomAttribute<EventBusExcuteAttribute>() != null).ToList();

            if (methods != null && methods.Count() == 0)
            {
                throw new VampirewalCoreEventBusException($"该事件工厂中没有找到标记EventBusExcuteAttribute特性的方法！");
            }
            else if (methods != null)
            {
                //new一个事件工厂上下文
                EventFactoryContext FactoryContexts = new EventFactoryContext();

                //这个foreach方法会并行执行，且会等待所有的方法执行完之后，再往下执行方法
                var result = Parallel.ForEach(methods, sub =>
                {
                    //new一个事件上下文，方便传递数据
                    EventContext context = new EventContext()
                    {
                        PassData = Params,
                        MethodName = sub.Name,
                        ExcutingTime = DateTime.Now
                    };
                    //将事件上下文添加到事件工厂的上下文集合中
                    FactoryContexts.EventContexts.Add(context);
                    //获取到当前方法上的AOP特性
                    var attrs = sub.GetCustomAttributes().Where(w => w is EventBusAOPAttribute).ToList();

                    try
                    {
                        //循环执行AOP特性的执行前方法
                        foreach (EventBusAOPAttribute item in attrs)
                        {
                            item.ExcuteBefore(context);
                        }

                        sub.Invoke(im, new object[] { context });
                    }
                    catch (Exception ex)
                    {
                        //捕获方法执行中的异常信息
                        context.ExceptionInfo = ex.InnerException;
                        //将事件工厂的是否全部完成成功标记为false
                        FactoryContexts.IsEventFactoryComplateSuccess = false;
                    }
                    finally
                    {
                        context.ExcutedTime = DateTime.Now;
                        //循环执行AOP特性的执行后方法
                        foreach (EventBusAOPAttribute item in attrs)
                        {
                            item.ExcuteAfter(context);
                        }
                    }
                });

                if (result.IsCompleted)
                {
                    //在事件工厂内的方法全部都执行完成之后，执行事件工厂的完成方法
                    im.EventFactoryComplate(FactoryContexts);
                }
            }
        }
        else
        {
            throw new VampirewalCoreEventBusException($"未找到{EventFactoryName}对应的事件工厂！");
        }
    }

    public void PublishParallel<TEventFactory>(object? Params = null) where TEventFactory : IVampirewalCoreEventFactory
    {
        Type type = typeof(TEventFactory);

        PublishParallel(type.Name, Params);
    }
}